#include "semantic.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

extern int filled[MAX_SYMBOL_COUNT];
extern Symbol* symbolTable[MAX_SYMBOL_COUNT];

void semanticProgram(Node* node) {
}

void semanticExtdefList(Node* node) {
}

void semanticExtdef(Node* extDef) {
    switch (extDef->ch[1]->elem) {
        case SybExtDecList : 
            fillSymbols(extDef->ch[0]->snode, extDef->ch[1]->snode);
            break;
        case SybSEMI : 
            fillStruct(extDef->ch[0]->snode);
            break;
        case SybFunDec : 
            fillFunctions(extDef->ch[0]->snode, extDef->ch[1]->snode);
            break;
        default : 
            break;
    }
}

void semanticExtDecList(Node* extdecList) {
    synAssign(extdecList->snode, extdecList->ch[0]->snode, NAMEVAL);
    synAssign(extdecList->snode, extdecList->ch[0]->snode, DATATYPE);
    if (extdecList->cnt > 1) {
        extdecList->snode->nexDec = extdecList->ch[2]->snode;
    }
}

void semanticSpecifier(Node* specifier) {
    synAssign(specifier->snode, specifier->ch[0]->snode, NAMEVAL);
    synAssign(specifier->snode, specifier->ch[0]->snode, DATATYPE);
}

void semanticStructSpecifier(Node* structSpecifier) {
    if (structSpecifier->cnt == 2) {
        int index = getSymbolIndex(structSpecifier->ch[1]->snode->name);
        if (index == -1 || !filled[index]) {
            serror(17, structSpecifier->ch[1]->lineno, "Undefined structure", structSpecifier->ch[1]->snode->name);
            udfAssign(structSpecifier->snode);
        } else {
            tabAssign(structSpecifier->snode, index, DATATYPE);
        }
    } else {
        stuAssign(structSpecifier->snode, structSpecifier);
    }
}

void semanticOptTag(Node* optTag) {
    synAssign(optTag->snode, optTag->ch[0]->snode, NAMEVAL);
}

void semanticTag(Node* tag) {
    synAssign(tag->snode, tag->ch[0]->snode, NAMEVAL);
}

void semanticVarDec(Node* varDec) {
    synAssign(varDec->snode, varDec->ch[0]->snode, NAMEVAL);
    synAssign(varDec->snode, varDec->ch[0]->snode, DATATYPE);
    if (varDec->cnt > 1) {
        if(varDec->snode->type == NULL) {
            lexAssign(varDec->snode, varDec->ch[2], ARRAYTYPE);
        } else {
            Type* arrayType = varDec->snode->type;
            while(arrayType->info.array.type != NULL) {
                arrayType = arrayType->info.array.type;
            }
            arrayType->info.array.type = malloc(sizeof(Type));
            arrayType->info.array.type->kind = ARRAY;
            arrayType->info.array.type->info.array.size = varDec->ch[2]->intval;
        }
    }
}

void semanticFunDec(Node* funDec) {
    synAssign(funDec->snode, funDec->ch[0]->snode, NAMEVAL);
    if (funDec->cnt > 3) {
        synAssign(funDec->snode, funDec->ch[2]->snode, ARGUMTYPE); // record first param
    }
}

void semanticVarList(Node* varList) {
    synAssign(varList->snode, varList->ch[0]->snode, NAMEVAL);
    synAssign(varList->snode, varList->ch[0]->snode, DATATYPE);

    if (varList->cnt > 1) {
        varList->snode->nexPar = varList->ch[2]->snode;
    }
}

void semanticParamDec(Node* paramDec) {
    typeAssign(paramDec->ch[0]->snode, paramDec->ch[1]->snode);
    synAssign(paramDec->snode, paramDec->ch[1]->snode, NAMEVAL);
    synAssign(paramDec->snode, paramDec->ch[1]->snode, DATATYPE);
    fillSymbol(paramDec->snode->name, paramDec->snode->type, 0); // insert when get

}

void semanticCompSt(Node* node) {
}

void semanticStmtList(Node* node) {
}

void semanticStmt(Node* stmt) {
    if (stmt->ch[0]->elem == SybRETURN) { // record return type
        Node* nd = stmt;
        while (nd->elem != SybExtdef) {
            nd = nd->pa;
        }
        if (!isOptrandTypeSame(nd->ch[0]->snode, stmt->ch[1]->snode)) {
            serror(8, stmt->ch[1]->lineno, "Type mismatched for return", "");
        }
    } else if (stmt->ch[0]->elem == SybIF || stmt->ch[0]->elem == SybWHILE) {
        if (!isOperandTypeInt(stmt->ch[2]->snode)) {
            serror(99, stmt->ch[2]->lineno, "Only int type can be used as condition", "");
        }
    }
}

void semanticDefList(Node* deflist) {
    synAssign(deflist->snode, deflist->ch[0]->snode, DATATYPE);
    deflist->snode->nexDec = deflist->ch[0]->snode->nexDec;

    if (deflist->cnt > 0) {
        deflist->snode->nexField = deflist->ch[1] == NULL ?  NULL : deflist->ch[1]->snode;
    }
}

void semanticDef(Node* def) {
    Node* nd = def;
    while (nd->elem != SybCompSt && nd->elem != SybStructSpecifier) {
        nd = nd->pa;
    }

    if (nd->elem == SybStructSpecifier) {
        if (nd->cnt > 2) { // define a structure
            fillStructSymbols(def->ch[0]->snode, def->ch[1]->snode);
        } else {
        }
    } else {
        if (def->ch[0]->snode->type->kind != UNDEFINED) {
            fillSymbols(def->ch[0]->snode, def->ch[1]->snode);
        }
    }
    synAssign(def->snode, def->ch[0]->snode, DATATYPE);
    def->snode->nexDec = def->ch[1]->snode;
}

void semanticDecList(Node* decList) {
    synAssign(decList->snode, decList->ch[0]->snode, NAMEVAL);
    synAssign(decList->snode, decList->ch[0]->snode, DATATYPE);

    if(decList->cnt > 1) {
        decList->snode->nexDec = decList->ch[2]->snode;
    }
}

void semanticDec(Node* dec) {
    if (dec->cnt > 1) {
        Node* nd = dec;
        while(nd->elem != SybDef) { // get variable type
            nd = nd->pa;
        }
        nd = nd->ch[0];
        if (!isOptrandTypeSame(nd->snode, dec->ch[2]->snode)) {
            serror(5, dec->ch[1]->lineno, "Type mismatched for assignment", "");
        }
    }
    synAssign(dec->snode, dec->ch[0]->snode, NAMEVAL);
    synAssign(dec->snode, dec->ch[0]->snode, DATATYPE);
}

void semanticExp(Node* exp) {
    switch (exp->cnt) {
        case 1 :
            if (exp->ch[0]->elem == SybID) {
                int index = getSymbolIndex(exp->ch[0]->snode->name);
                if (index == -1 || !filled[index]) {
                    serror(1, exp->ch[0]->lineno, "Undefined variable", exp->ch[0]->snode->name);
                    udfAssign(exp->ch[0]->snode);
                } else {
                    tabAssign(exp->ch[0]->snode, index, DATATYPE);
                }
            }
            synAssign(exp->snode, exp->ch[0]->snode, NAMEVAL);
            synAssign(exp->snode, exp->ch[0]->snode, DATATYPE);
            break;
        case 2 :
            if (exp->ch[0]->elem == SybMINUS) {
                if (!isOperandBasicType(exp->ch[1]->snode)) {
                    serror(98, exp->ch[1]->lineno, "Unsupport operand type", "");
                }
                synAssign(exp->snode, exp->ch[1]->snode, NAMEVAL);
                synAssign(exp->snode, exp->ch[1]->snode, DATATYPE);
            } else { // NOT
                if (!isOperandTypeInt(exp->ch[1]->snode)) {
                    serror(98, exp->ch[1]->lineno, "Unsupport operand type", "");
                }
                synAssign(exp->snode, exp->ch[1]->snode, NAMEVAL);
                synAssign(exp->snode, exp->ch[1]->snode, DATATYPE);
            }
            break;
        case 3 :
            if (isAsnOperator(exp->ch[1])) {
                if (!isLeftHandVar(exp->ch[0]->snode)) {
                    serror(6, exp->ch[1]->lineno, "The left-hand side of an assignment must be a variable", "");
                }
                if (!isOptrandTypeSame(exp->ch[0]->snode, exp->ch[2]->snode)) {
                    serror(5, exp->ch[1]->lineno, "Type mismatched for assignment", "");
                }
                synAssign(exp->snode, exp->ch[0]->snode, NAMEVAL);
                synAssign(exp->snode, exp->ch[0]->snode, DATATYPE);
            }
            else if (isAlgOperator(exp->ch[1])) {
                if (!isOperandBasicType(exp->ch[0]->snode) || !isOperandBasicType(exp->ch[2]->snode)) {
                    serror(98, exp->ch[1]->lineno, "Unsupport operand type", "");
                }
                if (!isOptrandTypeSame(exp->ch[0]->snode, exp->ch[2]->snode)) {
                    serror(7, exp->ch[1]->lineno, "Type mismatched for operands", "");
                }
                synAssign(exp->snode, exp->ch[0]->snode, NAMEVAL);
                synAssign(exp->snode, exp->ch[0]->snode, DATATYPE);
            } else if (isLogOperator(exp->ch[1])) {
                if(!isOperandTypeInt(exp->ch[0]->snode) || !isOperandTypeInt(exp->ch[2]->snode)) {
                    if (!(exp->ch[0]->snode->type->kind == UNDEFINED || exp->ch[2]->snode->type->kind == UNDEFINED)) {
                        serror(99, exp->ch[1]->lineno, "Type mismatched for logical operator", "");
                    }
                }
                synAssign(exp->snode, exp->ch[0]->snode, NAMEVAL);
                synAssign(exp->snode, exp->ch[0]->snode, DATATYPE);
            } else if (exp->ch[0]->elem == SybLP) { // (exp)
                synAssign(exp->snode, exp->ch[1]->snode, NAMEVAL);
                synAssign(exp->snode, exp->ch[1]->snode, DATATYPE);
            } else if (exp->ch[1]->elem == SybLP) { // no args function
                int index = getSymbolIndex(exp->ch[0]->snode->name);
                if (index == -1 || !filled[index]) {
                    serror(2, exp->ch[0]->lineno, "Undefined function", exp->ch[0]->snode->name);
                    udfAssign(exp->ch[0]->snode);
                } else {
                    tabAssign(exp->ch[0]->snode, index, DATATYPE);
                }
                synAssign(exp->snode, exp->ch[0]->snode, NAMEVAL);
                synAssign(exp->snode, exp->ch[0]->snode, DATATYPE);
            } else if (exp->ch[1]->elem == SybDOT) {
                if (!isVarStructure(exp->ch[0]->snode)) {
                    serror(13, exp->ch[1]->lineno, "Illegal use of", ".");
                    udfAssign(exp->snode);
                } else if(!isFieldDefined(exp->ch[0]->snode, exp->ch[2]->snode)) {
                    printf("Error type 14 at Line %d: Non-existent field \"%s\"\n", exp->ch[2]->lineno, exp->ch[2]->snode->name);
                    udfAssign(exp->snode);
                } else {
                    synAssign(exp->snode, exp->ch[2]->snode, NAMEVAL);
                    synAssign(exp->snode, exp->ch[2]->snode, DATATYPE);
                }
            }
            break;
        case 4 :
            if (exp->ch[2]->elem == SybArgs) {
                int index = getSymbolIndex(exp->ch[0]->snode->name);
                if (index == -1 || !filled[index]) {
                    serror(2, exp->ch[0]->lineno, "Undefined function", exp->ch[0]->snode->name);
                    udfAssign(exp->ch[0]->snode);
                } else if (symbolTable[index]->type->kind != FUNC_BASIC) {
                    printf("Error type 11 at Line %d: \"%s\" is not a function\n", exp->ch[0]->lineno, exp->ch[0]->snode->name);
                } else {
                    tabAssign(exp->ch[0]->snode, index, DATATYPE);
                    if (!checkFuncArgs(index, exp->ch[2]->snode)) {
                        printf("Error type 9 at Line %d: Function \"%s", exp->ch[0]->lineno, exp->ch[0]->snode->name);
                        showArgs(symbolTable[index]->args);
                        printf("\" is not applicable for arguments \"");
                        showArgs(exp->ch[2]->snode);
                        printf("\"\n");
                    }
                }
                synAssign(exp->snode, exp->ch[0]->snode, NAMEVAL);
                synAssign(exp->snode, exp->ch[0]->snode, DATATYPE);
            } else if (exp->ch[1]->elem == SybLB) { // deep array @3
                if (exp->ch[0]->snode->type->kind != ARRAY) {
                    printf("Error type 10 at Line %d: \"%s\" is not an array\n", exp->ch[0]->lineno, exp->ch[0]->snode->name);
                    udfAssign(exp->snode);
                } else if (exp->ch[2]->snode->type->info.basic != BASIC_INT) {
                    printf("Error type 12 at Line %d: \"%lf\" is not an integer\n", exp->ch[2]->lineno, exp->ch[2]->snode->type->constant.floatval);
                    udfAssign(exp->snode);
                } else if (exp->ch[0]->snode->type->info.array.size <= exp->ch[2]->snode->type->constant.intval) {
                    serror(97, exp->ch[2]->lineno, "Array out of index", "");
                    udfAssign(exp->snode);
                } else {
                    synAssign(exp->snode, exp->ch[0]->snode, NAMEVAL);
                    synAssign(exp->snode, exp->ch[0]->snode, DATATYPE);
                    Type* type = malloc(sizeof(Type));
                    type = exp->snode->type->info.array.type;
                    exp->snode->type = type;
                }
            }
            break;
    }
}

void semanticArgs(Node* args) {
    synAssign(args->snode, args->ch[0]->snode, DATATYPE);
    if (args->cnt > 1) {
        args->snode->nexPar = args->ch[2]->snode;
    }
}
